﻿using Aliphant.ChatAI.Interfaces;
using Aliphant.ChatAI.Models;
using Aliphant.ChatAI.Utility;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

using System.Drawing;
using System.Text;

namespace Aliphant.ChatAI.ChatProxys
{
    public abstract class ChatAIBase : IChatAI
    {
        protected HttpClient client;
        protected JObject requestBody;
        protected JArray messages;
        protected JArray historyMsg;
        protected ChatSetting chatSetting;
        protected readonly string filePath;
        protected readonly string currentChat;
        protected readonly string historyChat;
        protected readonly Thread _workerThread;
        protected readonly ManualResetEvent _pauseEvent;
        protected volatile bool _shouldStop = false;
        protected readonly Queue<string> _messageQueue;

        public event Action<ChatContent>? OutPutHandle;
        public event Action? InPutHandle;

        private CancellationTokenSource? _cts;

        public ChatAIBase(string filePath)
        {
            _messageQueue = new Queue<string>();
            _pauseEvent = new ManualResetEvent(false);
            chatSetting = new ChatSetting();
            messages = new JArray();
            historyMsg = new JArray();
            requestBody = new JObject();
            client = new HttpClient();

            _workerThread = new Thread(Run);

            this.filePath = filePath;
            this.currentChat = "chat.json";
            this.historyChat = "history.json";
        }

        public ChatAIBase(string filePath, string chatPath) : this(filePath)
        {
            if (!string.IsNullOrEmpty(chatPath))
            {
                this.currentChat = chatPath;
            }
        }

        #region Interfaces

        public void Start()
        {
            chatSetting = LoadSettings();
            messages = LoadChatList();
            requestBody = CreateRequestBody();
            client = CreateClient();

            SpeekHello(false);
            PrintHistoryChat();

            _shouldStop = false;
            _workerThread.Start();
        }

        public void Stop()
        {
            _cts?.Cancel();
            _shouldStop = true;
            _pauseEvent.Set();
            _workerThread.Join();
        }

        public void Pause()
        {
            _cts?.Cancel();
        }

        public void WeekUp()
        {
            _pauseEvent.Set();
        }

        public void AddMessage(string message)
        {
            _messageQueue.Enqueue(message);
        }

        #endregion

        protected async void Run()
        {
            while (!_shouldStop)
            {
                _pauseEvent.WaitOne();
                if (_shouldStop) break;

                try
                {
                    if (_messageQueue.Count == 0)
                    {
                        continue;
                    }

                    var question = _messageQueue.Dequeue();
                    if (question == "NEW")
                    {
                        messages = NewChat();
                        historyMsg = NewChat();
                        OutPutHandle?.Invoke(ChatContent.NEW);
                        SpeekHello(true);
                        //var content = new ChatContent("user", "打个招呼，20字以内，不少于10个字");
                        //await Send(content, true);
                    }
                    else
                    {
                        var content = new ChatContent("user", question);
                        _cts = new CancellationTokenSource();
                        await Send(content, _cts.Token);
                    }


                    InPutHandle?.Invoke();
                }
                catch
                {

                }
                _pauseEvent.Reset();
            }
        }

        protected async Task Send(ChatContent content, CancellationToken ct, bool isNew = false)
        {
            if (string.IsNullOrEmpty(content.Role))
            {
                return;
            }

            if (!isNew)
            {
                messages.Add(CreateMessage(content.Message, content.Role));
                historyMsg.Add(CreateMessage(content.Message, content.Role));
            }
            requestBody["messages"] = messages;
            string msgStr = JsonConvert.SerializeObject(requestBody).ToString();
            StringContent strCnt = new StringContent(msgStr, null, "application/json");

            try
            {
                var httpreq = new HttpRequestMessage(HttpMethod.Post, chatSetting.API) { Content = strCnt };
                var response = await client.SendAsync(httpreq, HttpCompletionOption.ResponseHeadersRead);
                response.EnsureSuccessStatusCode();
                if (response.IsSuccessStatusCode)
                {
                    var responseStream = await response.Content.ReadAsStreamAsync();
                    using var reader = new StreamReader(responseStream);

                    string? line;
                    StringBuilder? assistant = null;
                    StringBuilder? reasoning = null;
                    while (!reader.EndOfStream && !ct.IsCancellationRequested)
                    {
                        line = await reader.ReadLineAsync();
                        dynamic? json = AnalysisResponse(line);
                        if (json == null)
                        {
                            continue;
                        }

                        if (json.choices[0].delta.role != null)
                        {
                            assistant = new StringBuilder();
                        }
                        if (json.choices[0].delta.reasoning_content != null && reasoning == null)
                        {
                            reasoning = new StringBuilder();
                        }

                        //获取并输出内容
                        string cnt = json.choices[0].delta.content;
                        string rcnt = json.choices[0].delta.reasoning_content;
                        assistant?.Append(cnt);
                        reasoning?.Append(rcnt);
                        SendAnswer(cnt, rcnt, ((JObject)json).ToString(Formatting.Indented));
                    }

                    OutPutHandle?.Invoke(ChatContent.END);   //结束

                    if (ct.IsCancellationRequested)
                    {
                        return;
                    }

                    if (reasoning != null && !isNew)
                    {
                        historyMsg.Add(CreateMessage(reasoning.ToString().Trim('\r', '\n'), "reasoning"));
                    }
                    if (assistant != null)
                    {
                        messages.Add(CreateMessage(assistant.ToString().Trim('\r', '\n'), "assistant"));
                        historyMsg.Add(CreateMessage(assistant.ToString().Trim('\r', '\n'), "assistant"));

                        SaveChatLog(messages, "chat.json");
                        SaveChatLog(historyMsg, historyChat);
                    }
                }
            }
            catch (Exception ex)
            {
                OutPutHandle?.Invoke(new ChatContent(ex.ToString()));
            }
        }

        protected void OutputContent(string role, string message)
        {
            OutPutHandle?.Invoke(new ChatContent(role, message));
        }

        protected abstract void SendAnswer(string cnt, string rcnt, string? line);

        protected abstract JObject CreateRequestBody();

        protected virtual ChatSetting LoadSettings()
        {
            string fileName = Path.Combine(filePath, "setting.json");
            JsonHelper.ReadJson(fileName, out ChatSetting? settings);
            return settings ?? new ChatSetting();
        }

        protected virtual JArray LoadChatList()
        {
            string fileName = Path.Combine(filePath, currentChat);

            JArray? msgs = JsonHelper.ReadJsonArray(fileName);
            if (msgs != null)
            {
                return msgs;
            }

            msgs = new JArray();
            if (!string.IsNullOrEmpty(chatSetting.System))
            {
                msgs.Add(CreateMessage(chatSetting.System, "system"));
            }
            SaveChatLog(msgs, "chat.json");
            return msgs;
        }

        protected virtual JArray LoadHistoryChats()
        {
            string fileName = Path.Combine(filePath, historyChat);

            JArray? msgs = JsonHelper.ReadJsonArray(fileName);
            if (msgs != null)
            {
                return msgs;
            }

            msgs = new JArray();
            if (!string.IsNullOrEmpty(chatSetting.System))
            {
                msgs.Add(CreateMessage(chatSetting.System, "system"));
            }
            SaveChatLog(msgs, historyChat);
            return msgs;
        }

        protected virtual HttpClient CreateClient()
        {
            var client = new HttpClient();
            client.DefaultRequestHeaders.Add("Accept", "application/json");
            //client.DefaultRequestHeaders.Add("Authorization", "Bearer sk-0beec6bc7df141a1beeba900ecbb5939");
            client.DefaultRequestHeaders.Add("Authorization", "Bearer " + chatSetting.Key);
            return client;
        }

        protected virtual JArray NewChat()
        {
            chatSetting = LoadSettings();

            JArray messages = [];
            if (!string.IsNullOrEmpty(chatSetting.System))
            {
                messages.Add(CreateMessage(chatSetting.System, "system"));
            }
            SaveChatLog(messages, "chat.json");
            SaveChatLog(messages, historyChat);

            return messages;
        }

        protected void SaveChatLog(JArray messages, string file)
        {
            string fileName = Path.Combine(filePath, file);
            JsonHelper.SaveJArray(messages, fileName);
        }

        protected JObject? AnalysisResponse(string? response)
        {
            if (string.IsNullOrEmpty(response) ||
                response.Contains("[DONE]") ||
                response.Contains(": keep-alive"))
            {
                return null;
            }

            response = response.Substring(6);
            try
            {
                return JObject.Parse(response);
            }
            catch
            {
                return null;
            }
        }

        protected static JObject CreateMessage(string content, string role)
        {
            return new JObject
            {
                ["content"] = content,
                ["role"] = role
            };
        }

        protected void SpeekHello(bool stream = false)
        {
            if (stream)
            {
                char[] tokens = chatSetting.Tips.ToCharArray();
                foreach (var token in tokens)
                {
                    OutPutHandle?.Invoke(new ChatContent("assistant", token.ToString()));
                    Thread.Sleep(35);
                }
            }
            else
            {
                OutPutHandle?.Invoke(new ChatContent("assistant", chatSetting.Tips));
            }
            OutPutHandle?.Invoke(ChatContent.END);
        }

        public void PrintHistoryChat()
        {
            historyMsg = LoadHistoryChats();

            foreach (JObject message in historyMsg)
            {
                if (!message.TryGetValue("role", out JToken? roleToken) || !message.TryGetValue("content", out JToken? contentToken))
                {
                    continue;
                }

                string role = roleToken?.Value<string>() ?? "";
                string content = contentToken?.Value<string>() ?? "";
                switch (role)
                {
                    case "user":
                        OutPutHandle?.Invoke(new ChatContent("user", content));
                        break;
                    case "assistant":
                        OutPutHandle?.Invoke(new ChatContent("assistant", content));
                        break;
                    case "reasoning":
                        OutPutHandle?.Invoke(new ChatContent("reasoning", content));
                        break;
                }
                OutPutHandle?.Invoke(ChatContent.END);
            }
        }

        public virtual void UpdateAgent(GptConfig config)
        {

        }

        void IChatAI.NewSession()
        {

        }

        public void Load(GptConfig config)
        {
            throw new NotImplementedException();
        }

        public void RefreshSession()
        {
            throw new NotImplementedException();
        }
    }
}
